﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Threading.Tasks;
using CodeProject;
using FileSquirrel.Logic.XSD;

namespace FileSquirrel.Logic
{
    public class Indexer
    {
        private FileInfo _outputPath;
        private LibraryDataSet _dsFiles = new LibraryDataSet();
        private LibraryDataSet.LibraryRow _library;
        private LibraryDataSet.FilesDataTable _oldLibraryFiles;
        private IProgress<LibraryUpdateEventArguments> _progress;
        private LibraryUpdateEventArguments lea = new LibraryUpdateEventArguments();

        public Indexer(string LibName, string LibDir)
        {
            _outputPath = new FileInfo(Path.Combine(LibDir, string.Format("{0}.{1}", LibName, "fsl")));
            Reader._libdir = LibDir;
            Reader._libName = LibName;
        }

        public void GatherFiles(UserSettings.CategoryDataTable categories, Progress<LibraryUpdateEventArguments> libupprg)
        {
            _progress = libupprg;
            _oldLibraryFiles = Reader.GlobalLibrary._Files;
            _dsFiles = new LibraryDataSet();

            _library = _dsFiles.Library.AddLibraryRow(Environment.MachineName);

            Parallel.ForEach(categories, cat =>
            {
                DirectoryInfo di = new DirectoryInfo(cat.Path);
                if (di.Exists)
                {
                    foreach (var ext in cat.Extensions.Split(';'))
                    {
                        FileData[] files = FastDirectoryEnumerator.GetFiles(di.FullName, string.Format("*.{0}", ext), SearchOption.AllDirectories);

                        ProcessFiles(files, cat.Name);
                    }
                }
            });

            WriteLibary();
        }

        private void WriteLibary()
        {
            if (!_outputPath.Directory.Exists)
                _outputPath.Directory.Create();

            _dsFiles.WriteXml(_outputPath.FullName);
            Reader.GlobalLibrary = _dsFiles;
        }

        public void SaveRequest()
        {
            if (!_outputPath.Directory.Exists)
                _outputPath.Directory.Create();

            Reader.GlobalRequestLibrary.WriteXml(Path.Combine(_outputPath.Directory.FullName, RequestFilesName + ".fsr"));
        }

        private void ProcessFiles(FileData[] fis, string Category)
        {
            lea.CatName = Category;
            lea.Count = fis.Length;
            _progress.Report(lea);

            switch (Category.ToUpper())
            {
                case "SERIES":
                case "MOVIES":
                case "MUSIC":
                    ProcessVideo(fis, Category);
                    break;
                case "DATAFILES":
                    break;
                case "BOOKS":
                    break;
                default:
                    ProcessDefault(fis, Category);
                    break;
            }
        }

        private void ProcessVideo(IEnumerable<FileData> fis, string Category)
        {
            int iCount = 0;
            Parallel.ForEach(fis, FI =>
                                      {
                                          lock (_dsFiles)
                                          {
                                              LibraryDataSet.FilesRow libfile = _dsFiles._Files.NewFilesRow();

                                              if (!IndexerDelta(FI, ref libfile))
                                              {
                                                  GenericFileLib(FI, ref libfile, Category);

                                                  try
                                                  {
                                                      MediaInfoLib.MediaInfo mi = new MediaInfoLib.MediaInfo();
                                                      mi.Open(FI.Path);
                                                      libfile.Resolution = string.Format("{0} x {1}", mi.Get(MediaInfoLib.StreamKind.Video, 0, "Height"), mi.Get(MediaInfoLib.StreamKind.Video, 0, "Width"));

                                                      double duration = 0;
                                                      if (Double.TryParse(mi.Get(MediaInfoLib.StreamKind.Video, 0, "Duration"), out duration))
                                                      {
                                                          libfile.Duration = TimeSpan.FromMilliseconds(duration);
                                                      }
                                                      if (Double.TryParse(mi.Get(MediaInfoLib.StreamKind.Audio, 0, "Duration"), out duration))
                                                      {
                                                          libfile.Duration = TimeSpan.FromMilliseconds(duration);
                                                      }
                                                      mi.Close();
                                                  }
                                                  catch (BadImageFormatException ex)
                                                  {
                                                  }

                                                  if (Category.ToUpper() == "SERIES")
                                                  {
                                                      try
                                                      {
                                                          FileNameHelper fnh = new FileNameHelper(Path.GetFileNameWithoutExtension(FI.Path));

                                                          libfile.Show = string.IsNullOrEmpty(fnh.Show) ? new FileInfo(FI.Path).DirectoryName : fnh.Show;
                                                          libfile.Season = fnh.Season;
                                                          libfile.EpisodeNr = fnh.Episode;
                                                      }
                                                      catch (Exception)
                                                      {
                                                          libfile.Category = "Unknown";
                                                      }
                                                  }

                                                  _dsFiles._Files.AddFilesRow(libfile);
                                              }
                                              else
                                              {
                                                  _dsFiles._Files.ImportRow(libfile);
                                              }
                                              iCount++;
                                              lea.Index = iCount;
                                              _progress.Report(lea);
                                          }
                                      });
        }

        private bool IndexerDelta(FileData indFile, ref LibraryDataSet.FilesRow libFile)
        {
            DataRow[] foundRows = _oldLibraryFiles.Select(string.Format("Path = '{0}'", indFile.Path.Replace(@"'", "''")));

            if (foundRows.Length > 0)
            {
                libFile = (LibraryDataSet.FilesRow)foundRows[0];
            }
            return (foundRows.Length > 0);
        }

        private void ProcessDefault(FileData[] fis, string Category)
        {
            LibraryDataSet.FilesRow libfile;

            foreach (FileData FI in fis)
            {
                libfile = _dsFiles._Files.NewFilesRow();
                if (!IndexerDelta(FI, ref libfile))
                {
                    GenericFileLib(FI, ref libfile, Category);
                    _dsFiles._Files.AddFilesRow(libfile);
                }
                else
                {
                    _dsFiles._Files.ImportRow(libfile);
                }
            }
        }

        private void GenericFileLib(FileData FI, ref LibraryDataSet.FilesRow libfile, string Category)
        {
            libfile.LibraryRow = _library;
            libfile.Category = Category;
            libfile.DateModified = FI.LastWriteTime;
            libfile.Extension = Path.GetExtension(FI.Name);
            libfile.Name = Path.GetFileNameWithoutExtension(FI.Path);
            libfile.Path = FI.Path;
            libfile.Size = Convert.ToInt32((FI.Size / 1024) / 1024);
        }

        public string RequestFilesName { get; set; }
    }


    public class LibraryUpdateEventArguments
    {
        public int Index { get; set; }

        public int Count { get; set; }

        public string CatName { get; set; }

    }
}